#include <bits/stdc++.h>

using namespace std;

#define FREQ 240

// 对于所有边的源节点，在并行度为 p 的情况下，bank 数为 k 时，执行所有要的时钟周期数
// 理想时钟周期为：边数/p

void bank_conflict_cycles (int p, int bk, int edge_num, int *source_list) {
    int ideal_cycles = edge_num / p;
    int conflict_cycles = 0;
    int conflict_cycles_with_filter = 0;

    int shuffle [bk];
    int shuffle_with_filter [bk];
    int max_cycles = 0;
    int max_cycles_with_filter = 0;
    for (int i = 0; i < edge_num; i += p) {
        for (int j = 0; j < bk; j ++) {
            shuffle[j] = 0;
            shuffle_with_filter[j] = 0;
        }
        max_cycles = 0;
        max_cycles_with_filter = 0;
        // shuffle 当前 p 个地址
        for (int j = 0; j < p && i + j < edge_num; j ++) {

            shuffle[source_list[i + j] % bk] += 1;

            int shuffle_valid = 1;
            // 检测当前地址是否等于前面地址
            for (int k = 0; k < j; k ++) {
                if (source_list[i + j] == source_list[i + k]) {
                    shuffle_valid = 0;
                }
            }
            if (shuffle_valid == 1) {
                shuffle_with_filter[source_list[i + j] % bk] += 1;
            }
        }
        // 取最大值作为该轮地址的访存周期
        for (int j = 0; j < bk; j ++) {
            max_cycles = (shuffle[j] > max_cycles ? shuffle[j] : max_cycles);
            max_cycles_with_filter = (shuffle_with_filter[j] > max_cycles_with_filter ? shuffle_with_filter[j] : max_cycles_with_filter);
        }
        conflict_cycles += max_cycles;
        conflict_cycles_with_filter += max_cycles_with_filter;
    }

    cout << "[INFO] Conflict_Cycles = " << conflict_cycles << ", Ideal_Cycles = " << ideal_cycles << ", Filter_Cycles = " << conflict_cycles_with_filter << endl;
    cout << "[INFO] Performance = " << (edge_num * 1.0) / conflict_cycles * FREQ / 3000 << " GTEPs." << endl;
}

int main (int argc, char **argv) {

    int *source_list;
    int edge_num, pipe_num, bram_num;
    FILE *info_fp;

    if (argc != 4) {
        cout << "Usage: Parameter (info_file, bram_num, pipe_num)." << endl;
        exit (-1);
    }
    pipe_num = atoi (argv[3]);
    bram_num = atoi (argv[2]);
    cout << "[INFO] Pipe_num = " << pipe_num << ", Bram_num = " << bram_num << endl;

    if ((info_fp = fopen (argv[1], "r")) == NULL) {
        cout << "[ERROR] Failed to open file " << argv[1] << endl;
        exit (-1);
    }
    cout << "[INFO] Open file " << argv[1] << endl;

    fscanf (info_fp, "%d", &edge_num);
    // fseek (info_fp, 0, SEEK_SET);
    // fread (&edge_num, sizeof(int), 1, info_fp);
    cout << "[INFO] Edge_num = " << edge_num << endl;

    source_list = (int *) calloc (edge_num, sizeof(int));

    cout << "[INFO] Read edge list." << endl;
    for (int i = 0; i < edge_num; i ++) {
        fscanf (info_fp, "%d", &(source_list[i]));
    }
    // Use fread
    // fread (source_list, sizeof(int), edge_num, info_fp);
    cout << "[INFO] Last edge list " << source_list[edge_num - 1] << endl;
    
    cout << "[INFO] Read edge list complete." << endl;

    bank_conflict_cycles (pipe_num, bram_num, edge_num, source_list);

    return 0;
}